package com.beardedhen.harmonybootstrap;

import com.beardedhen.harmonybootstrap.api.attributes.BootstrapBrand;
import com.beardedhen.harmonybootstrap.api.attributes.ViewGroupPosition;
import com.beardedhen.harmonybootstrap.api.defaults.ButtonMode;
import com.beardedhen.harmonybootstrap.api.defaults.DefaultBootstrapBrand;
import com.beardedhen.harmonybootstrap.api.defaults.DefaultBootstrapSize;
import com.beardedhen.harmonybootstrap.api.view.*;
import com.beardedhen.harmonybootstrap.utils.DimenUtils;
import com.beardedhen.harmonybootstrap.utils.UiUtil;
import com.beardedhen.harmonybootstrap.utils.ViewUtils;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.ComponentState;
import ohos.agp.components.Text;
import ohos.agp.components.element.Element;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;

/**
 * BootstrapButtons are regular buttons styled with BootstrapBrand colors, roundable corners, and an
 * 'outlineable' mode. It is possible to group together multiple buttons using BootstrapButtonGroup,
 * allowing the use of different selection modes e.g. Checkbox/Radio group.
 */
public class BootstrapButton extends Text implements BootstrapSizeView, OutlineableView, RoundableView, ButtonModeView, BadgeContainerView, BootstrapBadgeView, BootstrapBrandView, Component.ComponentStateChangedListener {

    private BootstrapBrand bootstrapBrand;
    private int[] colorList;

    private TouchEventListener mTouchListner = new TouchEventListener() {
        @Override
        public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
            switch (buttonMode) {
                case REGULAR:
                    return false;
                case TOGGLE:
                    return handleToggle(touchEvent);
                case CHECKBOX:
                    return handleToggle(touchEvent);
                case RADIO:
                    return handleRadioEvent(touchEvent);
                default:
                    return false;
            }
        }
    };

    @Override
    public void onComponentStateChanged(Component component, int state) {
        switch (state) {
            case ComponentState.COMPONENT_STATE_HOVERED :
            case ComponentState.COMPONENT_STATE_CHECKED:
            case ComponentState.COMPONENT_STATE_FOCUSED:
            case ComponentState.COMPONENT_STATE_PRESSED:
            case ComponentState.COMPONENT_STATE_SELECTED:
            case 18432:
                setTextColor(new Color(colorList[1]));
                break;
            case ComponentState.COMPONENT_STATE_DISABLED:
                setTextColor(new Color(colorList[2]));
                break;
            default:
                setTextColor(new Color(colorList[0]));
                break;
        }

    }

    /**
     * instances of this can be used with .setOnCheckedChangedLisener to notify you when the state of a radio, togle or checkbox button has changed.
     */
    public interface OnCheckedChangedListener{
        /**
         * This method will get called when the state of a radio button, checkbox or toggle button changes.
         * @param bootstrapButton the view thats state is changing
         * @param isChecked weather the button is checked or not.
         */
        public void OnCheckedChanged(BootstrapButton bootstrapButton, boolean isChecked);
    }

    private static final String BOOTSTRAP_BUTTON_ROUNDED_CORNERS = "roundedCorners";
    private static final String BOOTSTRAP_BUTTON_SHOW_OUTLINE = "showOutline";
    private static final String BOOTSTRAP_BUTTON_CHECKED = "checked";
    private static final String BOOTSTRAP_BUTTON_BADGE_TEXT = "badgeText";
    private static final String BOOTSTRAP_BUTTON_BUTTON_MODE = "buttonMode";
    private static final String BOOTSTRAP_BUTTON_BOOTSTRAP_SIZE = "bootstrapSize";
    private static final String BOOTSTRAP_BUTTON_BOOTSTRAP_BRAND = "bootstrapBrand";

    private int parentIndex;
    private ViewGroupPosition viewGroupPosition = ViewGroupPosition.SOLO;
    private ButtonMode buttonMode = ButtonMode.REGULAR;

    private float bootstrapSize;

    private boolean roundedCorners;
    private boolean showOutline;
    private boolean mustBeSelected;

    private float baselineFontSize;
    private float baselineVertPadding;
    private float baselineHoriPadding;
    private float baselineStrokeWidth;
    private float baselineCornerRadius;
    private BootstrapBadge bootstrapBadge;
    private String badgeText;

    private OnCheckedChangedListener onCheckedChangedListener;

    public BootstrapButton(Context context) throws NotExistException, WrongTypeException, IOException {
        super(context);
        initialise(null);
    }

    public BootstrapButton(Context context, AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        super(context, attrs);
        initialise(attrs);
    }

    public BootstrapButton(Context context, AttrSet attrs, String defStyle) throws NotExistException, WrongTypeException, IOException {
        super(context, attrs, defStyle);
        initialise(attrs);
    }

    private void initialise(AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        if(attrs == null) {
            return;
        }
        setComponentStateChangedListener(this::onComponentStateChanged);
        viewGroupPosition = ViewGroupPosition.SOLO;

        try {
            this.roundedCorners = attrs.getAttr(BOOTSTRAP_BUTTON_ROUNDED_CORNERS).isPresent() ? attrs.getAttr(
                BOOTSTRAP_BUTTON_ROUNDED_CORNERS).get().getBoolValue() : false;

            this.showOutline = attrs.getAttr(BOOTSTRAP_BUTTON_SHOW_OUTLINE).isPresent() ? attrs.getAttr(
                BOOTSTRAP_BUTTON_SHOW_OUTLINE).get().getBoolValue() : false;

            this.mustBeSelected = attrs.getAttr(BOOTSTRAP_BUTTON_CHECKED).isPresent() ? attrs.getAttr(
                BOOTSTRAP_BUTTON_CHECKED).get().getBoolValue() : false;

            this.badgeText = attrs.getAttr(BOOTSTRAP_BUTTON_BADGE_TEXT).isPresent() ? attrs.getAttr(
                BOOTSTRAP_BUTTON_BADGE_TEXT).get().getStringValue() : null;

            int typeOrdinal = -1;
            if (attrs.getAttr(BOOTSTRAP_BUTTON_BOOTSTRAP_BRAND).isPresent()) {
                String typeOrdinalStr = attrs.getAttr(BOOTSTRAP_BUTTON_BOOTSTRAP_BRAND).get().getStringValue().trim();
                typeOrdinal = UiUtil.getBootstrapBrandInt(typeOrdinalStr);
            }

            int sizeOrdinal = -1;
            if (attrs.getAttr(BOOTSTRAP_BUTTON_BOOTSTRAP_SIZE).isPresent()) {
                String sizeOrdinalStr = attrs.getAttr(BOOTSTRAP_BUTTON_BOOTSTRAP_SIZE).get().getStringValue().trim();
                sizeOrdinal = UiUtil.getBootstrapSizeInt(sizeOrdinalStr);
            }

            int modeOrdinal = -1;
            if (attrs.getAttr(BOOTSTRAP_BUTTON_BUTTON_MODE).isPresent()) {
                String modeOrdinalStr = attrs.getAttr(BOOTSTRAP_BUTTON_BUTTON_MODE).get().getStringValue().trim();
                modeOrdinal = UiUtil.getBootstrapButtonModeInt(modeOrdinalStr);
            }

            bootstrapSize = DefaultBootstrapSize.fromAttributeValue(sizeOrdinal).scaleFactor();

            buttonMode = ButtonMode.fromAttributeValue(modeOrdinal);

            this.bootstrapBrand = DefaultBootstrapBrand.fromAttributeValue(typeOrdinal);


        } catch (Exception e) {
            e.printStackTrace();
        }

        baselineFontSize = DimenUtils.pixelsFromSpResource(getContext(), ResourceTable.Float_bootstrap_button_default_font_size);

        baselineVertPadding = DimenUtils.pixelsFromSpResource(getContext(), ResourceTable.Float_bootstrap_button_default_vert_padding);

        baselineHoriPadding = DimenUtils.pixelsFromSpResource(getContext(), ResourceTable.Float_bootstrap_button_default_hori_padding);

        baselineStrokeWidth = DimenUtils.pixelsFromSpResource(getContext(), ResourceTable.Float_bootstrap_button_default_edge_width);

        baselineCornerRadius = DimenUtils.pixelsFromSpResource(getContext(), ResourceTable.Float_bootstrap_button_default_corner_radius);

        if (badgeText != null) {
            setBadge(new BootstrapBadge(getContext(),attrs));
            setBadgeText(badgeText);

        }

        this.setTouchEventListener(mTouchListner);

        updateBootstrapState();
    }

    protected void updateBootstrapState() {

        bootstrapBrand = getBootstrapBrand();

        float cornerRadius = baselineCornerRadius;
        float strokeWidth = baselineStrokeWidth;
        final float fontSize = baselineFontSize * bootstrapSize;

        setTextSize((int)fontSize);
        cornerRadius *= bootstrapSize;
        strokeWidth *= bootstrapSize;

        colorList = BootstrapDrawableFactory.bootstrapButtonText(getContext(), showOutline, bootstrapBrand);
        setTextColor(new Color(colorList[0]));

        Element bg = BootstrapDrawableFactory.bootstrapButton(
                getContext(),
                bootstrapBrand,
                (int) strokeWidth,
                (int) cornerRadius,
                viewGroupPosition,
                showOutline,
                roundedCorners);

        ViewUtils.setBackgroundDrawable(this, bg);

        int vert = (int) (baselineVertPadding * bootstrapSize);
        int hori = (int) (baselineHoriPadding * bootstrapSize);

        setPadding(hori, vert, hori, vert);
    }

    @Override
    public void setSelected(boolean selected) {
        super.setSelected(selected);
        if (onCheckedChangedListener != null) {
            onCheckedChangedListener.OnCheckedChanged(this, selected);
        }
    }

    private boolean handleRadioEvent( TouchEvent event) {
        if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            setSelected(true);

            ComponentParent parent = getComponentParent();

            if (parent instanceof BootstrapButtonGroup) {
                ((BootstrapButtonGroup) parent).onRadioToggle(parentIndex);
            }
            return true;
        }
        else {
            return false;
        }
    }

    private boolean handleToggle( TouchEvent event) {
        if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            setSelected(!isSelected());
            return true;
        }
        else {
            return false;
        }
    }

    void setViewGroupPosition(ViewGroupPosition viewGroupPosition, int parentIndex) {
        this.viewGroupPosition = viewGroupPosition;
        this.parentIndex = parentIndex;
        updateBootstrapState();
    }

    void updateFromParent(BootstrapBrand bootstrapBrand,
                          float bootstrapSize,
                          ButtonMode buttonMode,
                          boolean outline,
                          boolean rounded) {

        this.bootstrapSize = bootstrapSize;
        this.buttonMode = buttonMode;
        this.showOutline = outline;
        this.roundedCorners = rounded;
        setBootstrapBrand(bootstrapBrand);
        updateBootstrapState();
    }

    public boolean isMustBeSelected() {
        return mustBeSelected;
    }

    /**
     * Called by the ViewParent, notifies the child of its position so that it can update its
     * drawable to match the position
     *
     *  viewGroupPosition the position in the ViewGroup
     */
    @Override public void displayBadgeDrawable() {
        if (bootstrapBadge != null) {
            Element badgeDrawable = this.bootstrapBadge.getBadgeDrawable();

            if (badgeDrawable != null) {
                setAroundElements(
                        null,
                        null, badgeDrawable,
                        null);
                setAroundElementsPadding(DimenUtils.dpToPixels(getContext(),4));
            }
        }
    }

    /**
     * NOTE this method only works if the buttons mode is not set to regular.
     * for non Toggle, checkbox and radio see {@link BootstrapButton#}
     * @param listener OnCheckedChangedListener that will be fired when the schecked state ofa button is changed.
     */
    public void setOnCheckedChangedListener(OnCheckedChangedListener listener){
        onCheckedChangedListener = listener;
    }

    /*
     * Getters/Setters
     */

    @Override public boolean isShowOutline() {
        return showOutline;
    }

    @Override public boolean isRounded() {
        return roundedCorners;
    }

    @Override public void setShowOutline(boolean showOutline) {
        this.showOutline = showOutline;
        updateBootstrapState();
    }

    @Override public void setRounded(boolean rounded) {
        this.roundedCorners = rounded;
        updateBootstrapState();
    }

    @Override public ButtonMode getButtonMode() {
        return buttonMode;
    }

    @Override public void setButtonMode( ButtonMode buttonMode) {
        this.buttonMode = buttonMode;
    }

    @Override public void setBadge(BootstrapBadge badge) {
        this.bootstrapBadge = badge;
        this.bootstrapBadge.setBootstrapBrand(getBootstrapBrand(), true);
        this.bootstrapBadge.setBootstrapSize(getBootstrapSize());
        displayBadgeDrawable();
    }

    @Override
    public String getBadgeText() {
        return bootstrapBadge != null ? bootstrapBadge.getBadgeText() : null;
    }

    @Override
    public void setBadgeText( String badgeText) {
        if (bootstrapBadge != null) {
            this.badgeText = badgeText;
            this.bootstrapBadge.setBadgeText(this.badgeText);
            displayBadgeDrawable();
        }
    }

    @Override public float getBootstrapSize() {
        return bootstrapSize;
    }

    @Override public BootstrapBadge getBootstrapBadge() {
        return bootstrapBadge;
    }

    @Override public void setBootstrapSize(DefaultBootstrapSize bootstrapSize) {
        setBootstrapSize(bootstrapSize.scaleFactor());
    }

    @Override public void setBootstrapSize(float bootstrapSize) {
        this.bootstrapSize = bootstrapSize;
        updateBootstrapState();
    }

    @Override
    public void setBootstrapBrand(BootstrapBrand bootstrapBrand) {
        this.bootstrapBrand = bootstrapBrand;
        updateBootstrapState();
    }

    @Override
    public BootstrapBrand getBootstrapBrand() {
        return bootstrapBrand;
    }
}
